﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing.Drawing2D;

namespace PIF1006_TP2
{
    public static class Chiffrement
    {
        private static string END_CHAR = "\0";
        private static string VERBATIM_END_CHAR = "\\0";
        private static string CBCencryptedString;
        private static byte VI = 31;

        /*
         * Obtenir l'array de byte à partir d'une string
         */
        private static byte[] GetBytes(string str)
        {
            int charSize = sizeof(char);
            byte[] bytes = new byte[str.Length * charSize];
            System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
            return bytes;
        }

        /*
         * Obtenir la string à partir d'un array de bytes 
         */
        private static string GetString(byte[] bytes)
        {
            int charSize = sizeof(char);
            char[] chars = new char[bytes.Length / charSize];
            System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
            return new string(chars);
        }

        private static string ChiffrerParTransposer(string message, string cle)
        {
            string messageTranspose = String.Empty;
            int sizeX = cle.Length;
            int sizeY = (int)Math.Ceiling((double)message.Length / (double)sizeX);

            char[,] tableauDeTransposition = new char[sizeX, sizeY];

            int ctrMessage = 0;
            for (int ctrY = 0; ctrY < tableauDeTransposition.GetLength(1); ctrY++)
                for (int ctrX = 0; ctrX < tableauDeTransposition.GetLength(0); ctrX++)
                {
                    tableauDeTransposition[ctrX, ctrY] = (ctrMessage < message.Length) ? message[ctrMessage++] : END_CHAR[0];
                }

            char[,] tableauTranspose = new char[sizeX, sizeY];

            for (int ctrX = 0; ctrX < sizeX; ctrX++)
            {
                int xDestination = Convert.ToInt32(cle[ctrX].ToString()) - 1;
                for (int ctrY = 0; ctrY < tableauTranspose.GetLength(1); ctrY++)
                {
                    tableauTranspose[xDestination, ctrY] = tableauDeTransposition[ctrX, ctrY];
                }
            }

            for (int ctrX = 0; ctrX < tableauTranspose.GetLength(0); ctrX++)
                for (int ctrY = 0; ctrY < tableauTranspose.GetLength(1); ctrY++)
                {
                    messageTranspose += tableauTranspose[ctrX, ctrY];
                }

            return messageTranspose.Replace(END_CHAR, VERBATIM_END_CHAR);
        }

        private static string DechiffrerParTransposer(string message, string cle)
        {
            message = message.Replace(VERBATIM_END_CHAR, END_CHAR);
            string messageTranspose = String.Empty;
            int sizeX = cle.Length;
            int sizeY = (int)Math.Ceiling((double)message.Length / (double)sizeX);

            char[,] tableauDeTransposition = new char[sizeX, sizeY];

            int ctrMessage = 0;
            for (int ctrX = 0; ctrX < tableauDeTransposition.GetLength(0); ctrX++)
                for (int ctrY = 0; ctrY < tableauDeTransposition.GetLength(1); ctrY++)
                {
                    tableauDeTransposition[ctrX, ctrY] = message[ctrMessage++];
                }

            char[,] tableauTranspose = new char[sizeX, sizeY];

            for (int ctrX = 0; ctrX < sizeX; ctrX++)
            {
                int xSource = Convert.ToInt32(cle[ctrX].ToString()) - 1;
                for (int ctrY = 0; ctrY < tableauTranspose.GetLength(1); ctrY++)
                {
                    tableauTranspose[ctrX, ctrY] = tableauDeTransposition[xSource, ctrY];
                }
            }

            for (int ctrY = 0; ctrY < tableauTranspose.GetLength(1); ctrY++)
                for (int ctrX = 0; ctrX < tableauTranspose.GetLength(0); ctrX++)
                {
                    messageTranspose += tableauTranspose[ctrX, ctrY];
                }

            return messageTranspose;
        }

        private static string ChiffrerParCBC(string message)
        {
            byte vi = VI;

            byte[] bytesMessageClear = GetBytes(message);
            byte[] bytesMessageEncrypted = new byte[bytesMessageClear.Length];

            for (int ctr = 0; ctr < bytesMessageClear.Length; ctr++)
            {
                bytesMessageEncrypted[ctr] = (byte)(bytesMessageClear[ctr] ^ vi);
                vi = bytesMessageEncrypted[ctr];
            }

            CBCencryptedString = GetString(bytesMessageEncrypted);
            return CBCencryptedString.Replace(END_CHAR, VERBATIM_END_CHAR);
        }

        private static string DechiffrerParCBC(string message)
        {
            byte[] bytesMessageEncrypted = String.IsNullOrEmpty(CBCencryptedString) ? GetBytes(message) : GetBytes(CBCencryptedString);
            byte vi = VI;

            byte[] bytesMessageClear = new byte[bytesMessageEncrypted.Length];
            for (int ctr = 0; ctr < bytesMessageEncrypted.Length; ctr++)
            {
                bytesMessageClear[ctr] = (byte)(bytesMessageEncrypted[ctr] ^ vi);
                vi = bytesMessageEncrypted[ctr];
            }

            return GetString(bytesMessageClear).Replace(VERBATIM_END_CHAR, END_CHAR);
        }

        public static string Chiffrer(string message, string cle)
        {
            return ChiffrerParCBC(ChiffrerParTransposer(message, cle));
        }

        public static string Dechiffrer(string message, string cle)
        {
            return DechiffrerParTransposer(DechiffrerParCBC(message), cle);
        }
    }
}
